package com.mapflow.webapp.controller;

import java.util.Locale;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.MailException;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.AuthenticationTrustResolver;
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.mapflow.geo.common.constants.Constants;
import com.mapflow.geo.common.manager.RoleManager;
import com.mapflow.geo.common.manager.UserManager;
import com.mapflow.geo.common.model.Role;
import com.mapflow.geo.common.model.entities.User;
import com.mapflow.geo.common.service.UserExistsException;
import com.mapflow.webapp.util.RequestUtil;

/**
 * Implementation of <strong>SimpleFormController</strong> that interacts with
 * the {@link UserManager} to retrieve/persist values to the database.
 * <p>
 * <a href="UserFormController.java.html"><i>View Source</i></a>
 * 
 * @author <a href="mailto:matt@raibledesigns.com">Matt Raible</a>
 */
@Controller
@RequestMapping("/userform*")
public class UserFormController extends BaseFormController {

	private RoleManager roleManager;

	public UserFormController() {
		setCancelView("redirect:/mainMenu");
		setSuccessView("redirect:/admin/users");
	}

	protected boolean isAdd(final HttpServletRequest request) {
		final String method = request.getParameter("method");
		return ((method != null) && method.equalsIgnoreCase("add"));
	}

	private boolean isFormSubmission(final HttpServletRequest request) {
		return request.getMethod().equalsIgnoreCase("post");
	}

	@RequestMapping(method = RequestMethod.POST)
	public String onSubmit(final User user, final BindingResult errors,
			final HttpServletRequest request, final HttpServletResponse response)
			throws Exception {
		if (request.getParameter("cancel") != null) {
			if (!StringUtils.equals(request.getParameter("from"), "list")) {
				return getCancelView();
			} else {
				return getSuccessView();
			}
		}

		if (validator != null) { // validator is null during testing
			validator.validate(user, errors);

			if (errors.hasErrors() && (request.getParameter("delete") == null)) { // don't
																					// validate
																					// when
																					// deleting
				return "userform";
			}
		}

		log.debug("entering 'onSubmit' method...");

		final Locale locale = request.getLocale();

		if (request.getParameter("delete") != null) {
			getUserManager().removeUser(user.getId().toString());
			saveMessage(request,
					getText("user.deleted", user.getFullName(), locale));

			return getSuccessView();
		} else {

			// only attempt to change roles if user is admin for other users,
			// showForm() method will handle populating
			if (request.isUserInRole(Constants.ADMIN_ROLE)) {
				final String[] userRoles = request
						.getParameterValues("userRoles");

				if (userRoles != null) {
					user.getObjectRolesList().clear();
					for (final String roleName : userRoles) {
						user.addRole(roleManager.getRole(roleName));
					}
				}
			}

			final Integer originalVersion = user.getVersion();

			try {
				getUserManager().saveUser(user);
			} catch (final AccessDeniedException ade) {
				// thrown by UserSecurityAdvice configured in aop:advisor
				// userManagerSecurity
				log.warn(ade.getMessage());
				response.sendError(HttpServletResponse.SC_FORBIDDEN);
				return null;
			} catch (final UserExistsException e) {
				errors.rejectValue("username", "errors.existing.user",
						new Object[] { user.getUsername(), user.getEmail() },
						"duplicate user");

				// redisplay the unencrypted passwords
				user.setPassword(user.getConfirmPassword());
				// reset the version # to what was passed in
				user.setVersion(originalVersion);

				return "userform";
			}

			if (!StringUtils.equals(request.getParameter("from"), "list")) {
				saveMessage(request,
						getText("user.saved", user.getFullName(), locale));

				// return to main Menu
				return getCancelView();
			} else {
				if (StringUtils.isBlank(request.getParameter("version"))) {
					saveMessage(request,
							getText("user.added", user.getFullName(), locale));

					// Send an account information e-mail
					message.setSubject(getText("signup.email.subject", locale));

					try {
						sendUserMessage(
								user,
								getText("newuser.email.message",
										user.getFullName(), locale),
								RequestUtil.getAppURL(request));
					} catch (final MailException me) {
						saveError(request, me.getCause().getLocalizedMessage());
					}

					return getSuccessView();
				} else {
					saveMessage(
							request,
							getText("user.updated.byAdmin", user.getFullName(),
									locale));
				}
			}
		}

		return "userform";
	}

	@Autowired
	public void setRoleManager(final RoleManager roleManager) {
		this.roleManager = roleManager;
	}

	@ModelAttribute
	@RequestMapping(method = { RequestMethod.GET, RequestMethod.POST })
	protected User showForm(final HttpServletRequest request,
			final HttpServletResponse response) throws Exception {
		// If not an administrator, make sure user is not trying to add or edit
		// another user
		if (!request.isUserInRole(Constants.ADMIN_ROLE_SECURITY)
				&& !isFormSubmission(request)) {
			if (isAdd(request) || (request.getParameter("id") != null)) {
				response.sendError(HttpServletResponse.SC_FORBIDDEN);
				log.warn("User '" + request.getRemoteUser()
						+ "' is trying to edit user with id '"
						+ request.getParameter("id") + "'");

				throw new AccessDeniedException(
						"You do not have permission to modify other users.");
			}
		}

		if (!isFormSubmission(request)) {
			final String userId = request.getParameter("id");

			// if user logged in with remember me, display a warning that they
			// can't change passwords
			log.debug("checking for remember me login...");

			final AuthenticationTrustResolver resolver = new AuthenticationTrustResolverImpl();
			final SecurityContext ctx = SecurityContextHolder.getContext();

			if (ctx.getAuthentication() != null) {
				final Authentication auth = ctx.getAuthentication();

				if (resolver.isRememberMe(auth)) {
					request.getSession().setAttribute("cookieLogin", "true");

					// add warning message
					saveMessage(
							request,
							getText("userProfile.cookieLogin",
									request.getLocale()));
				}
			}

			User user;
			if ((userId == null) && !isAdd(request)) {
				user = getUserManager().getUserByUsername(
						request.getRemoteUser());
			} else if (!StringUtils.isBlank(userId)
					&& !"".equals(request.getParameter("version"))) {
				user = getUserManager().getUser(userId);
			} else {
				user = new User();
				user.addRole(new Role(Constants.USER_ROLE));
			}

			user.setConfirmPassword(user.getPassword());

			return user;
		} else {
			// populate user object from database, so all fields don't need to
			// be hidden fields in form
			return getUserManager().getUser(request.getParameter("id"));
		}
	}
}
